namespace NetFx.MSIL
{
  using System;
  using System.Collections.Generic;
  using System.Text;
  using System.Reflection;
  using System.Reflection.Emit;


  public enum IntrinsicTypes
  {
    UNKNOWN = 0x00,
    BOOLEAN = 0x02,
    CHAR = 0x03,
    I1 = 0x04,
    U1 = 0x05,
    I2 = 0x06,
    U2 = 0x07,
    I4 = 0x08,
    U4 = 0x09,
    I8 = 0x0a,
    U8 = 0x0b,
    R4 = 0x0c,
    R8 = 0x0d,
    STRING = 0x0e,
    I = 0x18,
    U = 0x19,
    OBJECT = 0x1c,
  }

  /// <summary>
  ///     //Type ::= ( BOOLEAN | CHAR | I1 | U1 | U2 | U2 | I4 | U4 | I8 | U8 | R4 | R8 | I | U | STRING| OBJECT
  ///                | VALUETYPE  TypeDefOrRefEncoded
  ///                | CLASS      TypeDefOrRefEncoded
  ///                | PTR CustomMod* VOID
  ///                | PTR CustomMod* Type
  ///                | FNPTR MethodDefSig
  ///                | FNPTR MethodRefSig
  ///                | ARRAY Type ArrayShapeSignature
  ///                | SZARRAY CustomMod* Type
  ///                | GENERICINST (CLASS | VALUETYPE) TypeDefOrRefEncoded GenArgCount Type*
  ///                | VAR Number
  ///                | MVAR Number
  /// </summary>
  public abstract class TypeSignature : Signature
  {
    #region Fields
    protected Type type = null;
    #endregion
    #region Properties
    public virtual Type Type
    {
      get
      {
        return this.type;
      }
    }
    public virtual bool HasType
    {
      get
      {
        return this.type != null;
      }
    }
    #endregion
    #region Constructors
    public TypeSignature()
      : this(null)
    {

    }
    public TypeSignature(Type type)
    {
      this.type = type;
    }
    #endregion
  }

  public class SimpleTypeSignature : TypeSignature
  {
    #region StaticFields
    public static readonly SimpleTypeSignature VoidType = of(IntrinsicTypes.UNKNOWN);
    #endregion
    #region Statics
    public static Type TranslateType(IntrinsicTypes type)
    {
      switch (type)
      {
        case IntrinsicTypes.BOOLEAN:
          return typeof(bool);
        case IntrinsicTypes.CHAR:
          return typeof(char);
        case IntrinsicTypes.I:
          return typeof(IntPtr);
        case IntrinsicTypes.I1:
          return typeof(SByte);
        case IntrinsicTypes.I2:
          return typeof(Int16);
        case IntrinsicTypes.I4:
          return typeof(Int32);
        case IntrinsicTypes.I8:
          return typeof(Int64);
        case IntrinsicTypes.OBJECT:
          return typeof(Object);
        case IntrinsicTypes.R4:
          return typeof(Single);
        case IntrinsicTypes.R8:
          return typeof(Double);
        case IntrinsicTypes.STRING:
          return typeof(String);
        case IntrinsicTypes.U:
          return typeof(UIntPtr);
        case IntrinsicTypes.U1:
          return typeof(Byte);
        case IntrinsicTypes.U2:
          return typeof(UInt16);
        case IntrinsicTypes.U4:
          return typeof(UInt32);
        case IntrinsicTypes.U8:
          return typeof(UInt64);
        default:
          return null;
      }
    }
    public static SimpleTypeSignature of(IntrinsicTypes type)
    {
      return new SimpleTypeSignature(type);
    }
    public static SimpleTypeSignature of(Type type)
    {
      return new SimpleTypeSignature(type);
    }
    public static SimpleTypeSignature of(int token)
    {
      return new SimpleTypeSignature(token);
    }
    #endregion
    #region Fields
    protected int token = 0;
    protected List<CustomModifierSignature> modifiers = null;
    #endregion
    #region Properties
    public virtual int TypeToken
    {
      get
      {
        return this.Type != null ?
          this.Type.MetadataToken :
          this.token;
      }
    }
    #endregion
    #region Constructors
    public SimpleTypeSignature(IntrinsicTypes type)
      : this(TranslateType(type))
    {

    }
    public SimpleTypeSignature(Type type)
      : base(type)
    {
      if ((this.type = type) != null)
      {
        this.token = type.MetadataToken;
      }
    }
    public SimpleTypeSignature(int token)
      : base()
    {
      this.token = token;
    }
    #endregion
  }

  public class PointerTypeSignature : TypeSignature
  {
    #region Statics
    public static PointerTypeSignature of(List<CustomModifierSignature> modifiers)
    {
      return new PointerTypeSignature(modifiers);
    }
    public static PointerTypeSignature of(TypeSignature underlyingType, List<CustomModifierSignature> modifiers)
    {
      return new PointerTypeSignature(underlyingType, modifiers);
    }
    #endregion
    #region Fields
    protected TypeSignature underlyingType = null;
    protected List<CustomModifierSignature> modifiers = null;
    #endregion
    #region Properties
    public virtual TypeSignature UnderlyingType
    {
      get
      {
        return this.underlyingType;
      }
    }
    public virtual List<CustomModifierSignature> Modifiers
    {
      get
      {
        return this.modifiers;
      }
    }
    #endregion
    #region Constructors
    public PointerTypeSignature(List<CustomModifierSignature> modifiers)
      : this(SimpleTypeSignature.VoidType, modifiers)
    {
    }
    public PointerTypeSignature(TypeSignature underlyingType, List<CustomModifierSignature> modifiers)
    {
      this.underlyingType = underlyingType;
      this.modifiers = modifiers;
    }
    #endregion
  }

  public class FunctionPointerTypeSignature : TypeSignature
  {
    #region Statics
    public static FunctionPointerTypeSignature of(MethodSignature method)
    {
      return new FunctionPointerTypeSignature(method);
    }
    #endregion
    #region Fields
    protected MethodSignature method = null;
    #endregion
    #region Properties
    public virtual MethodSignature Method
    {
      get
      {
        return this.method;
      }
    }
    #endregion
    #region Constructors
    public FunctionPointerTypeSignature(MethodSignature method)
    {
      this.method = method;
    }
    #endregion
  }

  public class ArrayTypeSignature : TypeSignature
  {
    #region Statics
    public static ArrayTypeSignature of(TypeSignature underlyingType, ArrayShapeSignature arrayShape)
    {
      return new ArrayTypeSignature(underlyingType, arrayShape);
    }
    public static ArrayTypeSignature of(TypeSignature underlyingType, ArrayShapeSignature arrayShape, List<CustomModifierSignature> modifiers)
    {
      return new ArrayTypeSignature(underlyingType, arrayShape, modifiers);
    }
    #endregion
    #region Fields
    protected TypeSignature underlyingType = null;
    protected ArrayShapeSignature arrayShape = null;
    protected List<CustomModifierSignature> modifiers = null;
    #endregion
    #region Properties
    public virtual TypeSignature UnderlyingType
    {
      get
      {
        return this.underlyingType;
      }
    }
    public virtual ArrayShapeSignature ArrayShape
    {
      get
      {
        return this.arrayShape;
      }
    }
    public virtual List<CustomModifierSignature> Modifiers
    {
      get
      {
        return this.modifiers;
      }
    }
    #endregion
    #region Constructors
    public ArrayTypeSignature(TypeSignature underlyingType, ArrayShapeSignature arrayShape):this(underlyingType,arrayShape,null)
    {
    }
    public ArrayTypeSignature(TypeSignature underlyingType, ArrayShapeSignature arrayShape, List<CustomModifierSignature> modifiers)
    {
      this.underlyingType = underlyingType;
      this.arrayShape = arrayShape;
      this.modifiers = modifiers;
    }
    #endregion
 }

  public enum VariableMode:int
  {
    Unknown         = 0x00,
    TypeVariable    = 0x01,
    MemberVariable  = 0x02,
  }

  public class VariableTypeSignature : TypeSignature
  {
    #region Statics
    public static VariableTypeSignature of(VariableMode mode, int token)
    {
      return new VariableTypeSignature(mode, token);
    }
    #endregion
    #region Fields
    protected int token = 0;
    protected VariableMode mode = VariableMode.Unknown;
    #endregion
    #region Properties
    public virtual int Token
    {
      get
      {
        return this.token;
      }
    }
    public virtual VariableMode Mode
    {
      get
      {
        return this.mode;
      }
    }
    #endregion
    #region Constructors
    public VariableTypeSignature(VariableMode mode, int token)
    {
      this.mode = mode;
      this.token = token;
    }
    #endregion
  }

  public class GenericTypeSignature : TypeSignature
  {
    #region Statics
    public static GenericTypeSignature of(List<TypeSignature> genericTypes)
    {
      return new GenericTypeSignature(genericTypes);
    }
    #endregion
    #region Fields
    protected List<TypeSignature> genericTypes = new List<TypeSignature>();
    #endregion
    #region Properties
    #endregion
    #region Constructors
    public GenericTypeSignature(List<TypeSignature> genericTypes)
    {
      this.genericTypes = genericTypes;
    }
    #endregion
  }
}
